import { Provide, Inject } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository, In } from 'typeorm';
import { TeamEntity } from '@entity/team.entity';
import { TeamTeacherEntity } from '@entity/teamTeacher.entity';
import { TeacherVolunteerTeamEntity } from '@entity/teacherVolunteerTeam.entity';
import { GroupEntity } from '@entity/group.entity';
import { TeacherEntity } from '@entity/teacher.entity';
import { StudentTeamEntity } from '@entity/studentTeam.entity';
import { AllocateResultDto } from '@dto/allocate.dto';
import { DownloadExcelService } from './common/downloadExcel';
import { resBuild } from '@utils/resBuild';

/**
 * 匹配项接口
 */
interface MatchItem {
  teamId: number;
  teamName: string;
  teacherId: number;
  teacherName: string;
  score: number;
  teamVolunteerSort?: number; // 队伍志愿次序
  teacherVolunteerSort?: number; // 教师志愿次序
}

@Provide()
export class AllocateService {
  @InjectEntityModel(TeamEntity)
  teamRepository: Repository<TeamEntity>;

  @InjectEntityModel(TeamTeacherEntity)
  teamTeacherRepository: Repository<TeamTeacherEntity>;

  @InjectEntityModel(TeacherVolunteerTeamEntity)
  teacherVolunteerRepository: Repository<TeacherVolunteerTeamEntity>;

  @InjectEntityModel(GroupEntity)
  groupRepository: Repository<GroupEntity>;

  @InjectEntityModel(TeacherEntity)
  teacherRepository: Repository<TeacherEntity>;

  @InjectEntityModel(StudentTeamEntity)
  studentTeamRepository: Repository<StudentTeamEntity>;

  @Inject()
  downloadExcelService: DownloadExcelService;

  /**
   * 执行师生双向匹配算法
   */
  async executeMatch(groupId: number) {
    // 1. 获取分组信息
    const group = await this.groupRepository.findOne({
      where: { groupId, delFlag: '0' },
    });

    if (!group) {
      throw new Error('分组不存在');
    }

    // 2. 清除之前的分配记录（重置状态，允许重新分配）
    await this.resetAllocateStatus(groupId);

    const teacherMax = group.teacherMax; // 教师最多带队数量，也是第一志愿分数

    // 3. 获取该分组下所有队伍及其志愿
    const allTeams = await this.teamRepository.find({
      where: { groupId, delFlag: '0' },
      relations: ['teamTeachers', 'teamTeachers.teacher'],
    });

    if (allTeams.length === 0) {
      throw new Error('该分组下没有队伍');
    }

    // 4. 过滤出需要自动分配的队伍（排除管理员手动分配的队伍）
    // 管理员手动分配的特征：team_teacher 表中存在 sort=0 且 selectFlag=1 的记录
    const manualAllocatedTeamIds = new Set<number>();
    allTeams.forEach(team => {
      const manualRecord = team.teamTeachers?.find(tt => tt.sort === 0 && tt.selectFlag === 1);
      if (manualRecord) {
        manualAllocatedTeamIds.add(team.teamId);
      }
    });

    // 只对未手动分配的队伍进行自动匹配
    const teams = allTeams.filter(t => !manualAllocatedTeamIds.has(t.teamId));

    if (teams.length === 0 && manualAllocatedTeamIds.size > 0) {
      // 所有队伍都已被管理员手动分配
      return resBuild.success('该分组下所有队伍均已手动分配，无需自动匹配');
    }

    // 5. 获取所有教师志愿
    const teacherVolunteers = await this.teacherVolunteerRepository.find({
      where: { groupId },
      relations: ['teacher', 'team'],
    });

    // 6. 构建志愿得分映射
    const teamVolunteerMap = new Map<string, number>(); // key: teamId_teacherId, value: sort
    const teacherVolunteerMap = new Map<string, number>(); // key: teamId_teacherId, value: sort

    // 构建队伍志愿映射
    teams.forEach(team => {
      team.teamTeachers?.forEach(tt => {
        const key = `${team.teamId}_${tt.teacherId}`;
        teamVolunteerMap.set(key, tt.sort);
      });
    });

    // 构建教师志愿映射
    teacherVolunteers.forEach(tv => {
      const key = `${tv.teamId}_${tv.teacherId}`;
      teacherVolunteerMap.set(key, tv.sort);
    });

    // 6. 获取该分组的所有教师（关联用户信息获取姓名）
    const teacherIds = group.teacherIds?.split(',').map(id => parseInt(id.trim())) || [];
    const teachers = await this.teacherRepository.find({
      where: { teacherId: In(teacherIds) },
      relations: ['user'],
    });

    // 构建教师名称映射
    const teacherNameMap = new Map<number, string>();
    teachers.forEach(t => {
      teacherNameMap.set(t.teacherId, t.user?.nickName || '未知教师');
    });

    // 7. 计算所有可能的匹配得分
    const matchItems: MatchItem[] = [];

    teams.forEach(team => {
      teacherIds.forEach(teacherId => {
        const key = `${team.teamId}_${teacherId}`;

        // 获取队伍对教师的志愿次序
        const teamVolunteerSort = teamVolunteerMap.get(key);
        // 获取教师对队伍的志愿次序
        const teacherVolunteerSort = teacherVolunteerMap.get(key);

        // 计算得分
        let teamScore = 0;
        let teacherScore = 0;

        if (teamVolunteerSort !== undefined) {
          // 第1志愿 = teacherMax分，第2志愿 = teacherMax-1分，依此类推
          teamScore = Math.max(0, teacherMax - teamVolunteerSort + 1);
        }

        if (teacherVolunteerSort !== undefined) {
          teacherScore = Math.max(0, teacherMax - teacherVolunteerSort + 1);
        }

        // 综合得分 = 队伍志愿分 + 教师志愿分 × 1.1
        const totalScore = teamScore + teacherScore * 1.1;

        // 只有有志愿的才参与匹配（得分 > 0）
        if (totalScore > 0) {
          matchItems.push({
            teamId: team.teamId,
            teamName: team.teamName,
            teacherId,
            teacherName: teacherNameMap.get(teacherId) || '',
            score: totalScore,
            teamVolunteerSort,
            teacherVolunteerSort,
          });
        }
      });
    });

    // 8. 按得分降序排序
    matchItems.sort((a, b) => b.score - a.score);

    // 9. 执行匹配分配
    const matchedTeamIds = new Set<number>(); // 已匹配的队伍
    const teacherMatchCount = new Map<number, number>(); // 教师已匹配的队伍数量
    const volunteerMatches: MatchItem[] = []; // 志愿匹配成功的记录

    // 初始化教师匹配计数（需要考虑管理员手动分配的名额）
    teacherIds.forEach(id => teacherMatchCount.set(id, 0));

    // 统计管理员手动分配占用的教师名额
    allTeams.forEach(team => {
      if (manualAllocatedTeamIds.has(team.teamId)) {
        const manualRecord = team.teamTeachers?.find(tt => tt.sort === 0 && tt.selectFlag === 1);
        if (manualRecord) {
          const teacherId = manualRecord.teacherId;
          const currentCount = teacherMatchCount.get(teacherId) || 0;
          teacherMatchCount.set(teacherId, currentCount + 1);
        }
      }
    });

    // 按得分依次分配
    for (const item of matchItems) {
      // 如果队伍已匹配，跳过
      if (matchedTeamIds.has(item.teamId)) {
        continue;
      }

      // 如果教师名额已满，跳过
      const currentCount = teacherMatchCount.get(item.teacherId) || 0;
      if (currentCount >= teacherMax) {
        continue;
      }

      // 匹配成功
      matchedTeamIds.add(item.teamId);
      teacherMatchCount.set(item.teacherId, currentCount + 1);
      volunteerMatches.push(item);
    }

    // 10. 处理剩余队伍，随机分配
    const unmatchedTeams = teams.filter(t => !matchedTeamIds.has(t.teamId));
    const randomMatches: MatchItem[] = [];

    // 找出还有名额的教师
    const availableTeachers = teacherIds.filter(id => {
      const count = teacherMatchCount.get(id) || 0;
      return count < teacherMax;
    });

    // 随机分配
    for (const team of unmatchedTeams) {
      if (availableTeachers.length === 0) {
        break; // 没有可用教师了
      }

      // 随机选择一个有名额的教师
      const randomIndex = Math.floor(Math.random() * availableTeachers.length);
      const teacherId = availableTeachers[randomIndex];

      randomMatches.push({
        teamId: team.teamId,
        teamName: team.teamName,
        teacherId,
        teacherName: teacherNameMap.get(teacherId) || '',
        score: 0,
      });

      matchedTeamIds.add(team.teamId);

      // 更新教师匹配计数
      const currentCount = teacherMatchCount.get(teacherId) || 0;
      teacherMatchCount.set(teacherId, currentCount + 1);

      // 如果教师名额已满，从可用列表中移除
      if (currentCount + 1 >= teacherMax) {
        availableTeachers.splice(randomIndex, 1);
      }
    }

    // 11. 更新数据库
    await this.updateDatabase(groupId, volunteerMatches, randomMatches);

    // 12. 返回结果
    const result: AllocateResultDto = {
      totalTeams: allTeams.length, // 总队伍数（包括手动分配的）
      volunteerMatchedTeams: volunteerMatches.length, // 志愿匹配的队伍数
      randomMatchedTeams: randomMatches.length, // 随机分配的队伍数
      unmatchedTeams: teams.length - matchedTeamIds.size, // 未匹配的队伍数（不包括手动分配的）
      matchDetails: [
        ...volunteerMatches.map(m => ({ ...m, matchType: 'volunteer' as const })),
        ...randomMatches.map(m => ({ ...m, matchType: 'random' as const })),
      ],
    };

    // 如果有手动分配的队伍，在结果中添加说明
    if (manualAllocatedTeamIds.size > 0) {
      result.unmatchedTeams = result.unmatchedTeams; // 未匹配数不变
      // 可以在这里添加手动分配的统计信息
    }

    return resBuild.data(result);
  }

  /**
   * 重置分配状态（清除之前的分配记录）
   * 注意：不会重置管理员手动分配的记录（sort = 0）
   */
  private async resetAllocateStatus(groupId: number): Promise<void> {
    await this.teamRepository.manager.transaction(async manager => {
      const teamRepo = manager.getRepository(TeamEntity);
      const teamTeacherRepo = manager.getRepository(TeamTeacherEntity);
      const teacherVolunteerRepo = manager.getRepository(TeacherVolunteerTeamEntity);

      // 1. 获取该分组下的所有队伍ID
      const teams = await teamRepo.find({
        where: { groupId, delFlag: '0' },
        select: ['teamId'],
      });
      const teamIds = teams.map(t => t.teamId);

      if (teamIds.length === 0) {
        return;
      }

      // 2. 查询管理员手动分配的队伍ID（sort = 0 且 selectFlag = 1）
      const manualAllocatedRecords = await teamTeacherRepo.find({
        where: {
          teamId: In(teamIds),
          sort: 0,
          selectFlag: 1,
        },
        select: ['teamId'],
      });
      const manualAllocatedTeamIds = manualAllocatedRecords.map(r => r.teamId);

      // 3. 需要重置的队伍ID = 所有队伍 - 管理员手动分配的队伍
      const teamIdsToReset = teamIds.filter(id => !manualAllocatedTeamIds.includes(id));

      if (teamIdsToReset.length === 0) {
        // 所有队伍都是管理员手动分配的，不需要重置
        return;
      }

      // 4. 重置非手动分配队伍的 team 表的 selectFlag 为 '0'
      await teamRepo.update(
        { teamId: In(teamIdsToReset) },
        { selectFlag: '0' }
      );

      // 5. 重置非手动分配队伍的 team_teacher 表的 selectFlag 为 0（排除 sort=0 的记录）
      // 使用原生查询，因为 TypeORM 的 update 不支持复杂的 WHERE 条件
      await manager.query(
        `UPDATE team_teacher SET select_flag = 0
         WHERE team_id = ANY($1) AND sort != 0`,
        [teamIdsToReset]
      );

      // 6. 删除之前随机分配生成的志愿记录（sort = 999）
      await teacherVolunteerRepo.delete({
        groupId,
        teamId: In(teamIds),
        sort: 999,
      });

      // 7. 删除之前随机分配生成的 team_teacher 记录（sort = 999）
      await teamTeacherRepo.delete({
        teamId: In(teamIds),
        sort: 999,
      });
    });
  }

  /**
   * 更新数据库
   */
  private async updateDatabase(
    groupId: number,
    volunteerMatches: MatchItem[],
    randomMatches: MatchItem[]
  ): Promise<void> {
    await this.teamRepository.manager.transaction(async manager => {
      const teamRepo = manager.getRepository(TeamEntity);
      const teamTeacherRepo = manager.getRepository(TeamTeacherEntity);
      const teacherVolunteerRepo = manager.getRepository(TeacherVolunteerTeamEntity);

      // 处理志愿匹配的队伍
      for (const match of volunteerMatches) {
        // 更新 team 表的 selectFlag
        await teamRepo.update(
          { teamId: match.teamId },
          { selectFlag: '1' }
        );

        // 检查 team_teacher 表中是否已存在记录
        const existingTeamTeacher = await teamTeacherRepo.findOne({
          where: { teamId: match.teamId, teacherId: match.teacherId },
        });

        if (existingTeamTeacher) {
          // 如果已存在，更新 selectFlag
          await teamTeacherRepo.update(
            { teamId: match.teamId, teacherId: match.teacherId },
            { selectFlag: 1 }
          );
        } else {
          // 如果不存在（教师有志愿但学生没有），插入新记录
          const newTeamTeacher = teamTeacherRepo.create({
            teamId: match.teamId,
            teacherId: match.teacherId,
            sort: match.teamVolunteerSort ?? 1000, // 学生没有填写志愿时使用 1000
            selectFlag: 1,
          });
          await teamTeacherRepo.save(newTeamTeacher);
        }
      }

      // 处理随机分配的队伍
      for (const match of randomMatches) {
        // 更新 team 表的 selectFlag
        await teamRepo.update(
          { teamId: match.teamId },
          { selectFlag: '1' }
        );

        // 检查 team_teacher 表中是否已存在记录
        const existingTeamTeacher = await teamTeacherRepo.findOne({
          where: { teamId: match.teamId, teacherId: match.teacherId },
        });

        if (existingTeamTeacher) {
          // 如果已存在，更新 selectFlag
          await teamTeacherRepo.update(
            { teamId: match.teamId, teacherId: match.teacherId },
            { selectFlag: 1 }
          );
        } else {
          // 如果不存在，插入新记录
          const newTeamTeacher = teamTeacherRepo.create({
            teamId: match.teamId,
            teacherId: match.teacherId,
            sort: 999, // 随机分配的没有志愿次序
            selectFlag: 1,
          });
          await teamTeacherRepo.save(newTeamTeacher);
        }

        // 检查 teacher_volunteer_team 表中是否已存在记录
        const existingTeacherVolunteer = await teacherVolunteerRepo.findOne({
          where: { groupId, teamId: match.teamId, teacherId: match.teacherId },
        });

        if (!existingTeacherVolunteer) {
          // 如果不存在，插入新记录
          const newTeacherVolunteer = teacherVolunteerRepo.create({
            groupId,
            teamId: match.teamId,
            teacherId: match.teacherId,
            sort: 999, // 随机分配的没有志愿次序
          });
          await teacherVolunteerRepo.save(newTeacherVolunteer);
        }
      }
    });
  }

  /**
   * 管理员手动分配（队伍分配给教师）
   */
  async manualAllocate(teamId: number, teacherId: number) {
    await this.teamRepository.manager.transaction(async manager => {
      const teamRepo = manager.getRepository(TeamEntity);
      const teamTeacherRepo = manager.getRepository(TeamTeacherEntity);

      // 1. 验证队伍是否存在
      const team = await teamRepo.findOne({
        where: { teamId, delFlag: '0' },
      });

      if (!team) {
        throw new Error('队伍不存在');
      }

      // 2. 验证教师是否存在
      const teacher = await this.teacherRepository.findOne({
        where: { teacherId, delFlag: '0' },
      });

      if (!teacher) {
        throw new Error('教师不存在');
      }

      // 3. 先清除该队伍的所有 selectFlag = 1 的记录
      await teamTeacherRepo.update(
        { teamId },
        { selectFlag: 0 }
      );
      await teamTeacherRepo.delete(
        { teamId, sort: 0 },
      );
      // 4. 检查是否已存在该队伍和教师的志愿记录
      const existingRecord = await teamTeacherRepo.findOne({
        where: { teamId, teacherId },
      });

      if (existingRecord) {
        // 如果存在，更新 sort = 0 和 selectFlag = 1
        await teamTeacherRepo.update(
          { teamId, teacherId },
          { sort: 0, selectFlag: 1 }
        );
      } else {
        // 如果不存在，创建新记录
        const newRecord = teamTeacherRepo.create({
          teamId,
          teacherId,
          sort: 0, // sort = 0 表示管理员手动分配
          selectFlag: 1,
        });
        await teamTeacherRepo.save(newRecord);
      }

      // 5. 更新 team 表的 selectFlag 为 '1'
      await teamRepo.update(
        { teamId },
        { selectFlag: '1' }
      );
    });

    return resBuild.success('手动分配成功');
  }

  /**
   * 取消队伍和教师的匹配关系
   */
  async cancelAllocate(teamId: number, teacherId: number) {
    await this.teamRepository.manager.transaction(async manager => {
      const teamRepo = manager.getRepository(TeamEntity);
      const teamTeacherRepo = manager.getRepository(TeamTeacherEntity);

      // 1. 验证队伍是否存在
      const team = await teamRepo.findOne({
        where: { teamId, delFlag: '0' },
      });

      if (!team) {
        throw new Error('队伍不存在');
      }

      // 2. 验证教师是否存在
      const teacher = await this.teacherRepository.findOne({
        where: { teacherId, delFlag: '0' },
      });

      if (!teacher) {
        throw new Error('教师不存在');
      }

      // 3. 检查是否存在该队伍和教师的匹配记录
      const existingRecord = await teamTeacherRepo.findOne({
        where: { teamId, teacherId, selectFlag: 1 },
      });

      if (!existingRecord) {
        throw new Error('该队伍和教师之间没有匹配关系');
      }

      // 4. 取消选择：将 selectFlag 设置为 0
      await teamTeacherRepo.update(
        { teamId, teacherId },
        { selectFlag: 0 }
      );

      // 5. 检查该队伍是否还有其他选中的教师
      const otherSelectedRecords = await teamTeacherRepo.find({
        where: { teamId, selectFlag: 1 },
      });

      // 6. 如果没有其他选中的教师，更新 team 表的 selectFlag 为 '0'
      if (otherSelectedRecords.length === 0) {
        await teamRepo.update(
          { teamId },
          { selectFlag: '0' }
        );
      }
    });

    return resBuild.success('取消选择成功');
  }

  /**
   * 获取该分组下还有空闲位置的教师列表
   */
  async getAvailableTeachers(groupId: number) {
    // 1. 获取分组信息
    const group = await this.groupRepository.findOne({
      where: { groupId, delFlag: '0' },
    });

    if (!group) {
      throw new Error('分组不存在');
    }

    const teacherMax = group.teacherMax; // 教师最大带队数

    // 2. 获取该分组的所有教师
    const teacherIds = group.teacherIds?.split(',').map(id => parseInt(id.trim())) || [];

    if (teacherIds.length === 0) {
      return resBuild.data([]);
    }

    const teachers = await this.teacherRepository.find({
      where: { teacherId: In(teacherIds), delFlag: '0' },
      relations: ['user'],
    });

    // 3. 统计每个教师已带的队伍数量
    const teacherTeamCounts = await this.teamTeacherRepository
      .createQueryBuilder('tt')
      .select('tt.teacher_id', 'teacherId')
      .addSelect('COUNT(*)', 'count')
      .innerJoin('team', 't', 't.team_id = tt.team_id')
      .where('t.group_id = :groupId', { groupId })
      .andWhere('t.del_flag = :delFlag', { delFlag: '0' })
      .andWhere('tt.select_flag = :selectFlag', { selectFlag: 1 })
      .groupBy('tt.teacher_id')
      .getRawMany();

    // 构建教师带队数映射
    const teacherCountMap = new Map<number, number>();
    teacherTeamCounts.forEach(item => {
      teacherCountMap.set(parseInt(item.teacherId), parseInt(item.count));
    });

    // 4. 查询所有教师的志愿数据
    const allTeacherVolunteers = await this.teacherVolunteerRepository.find({
      where: { groupId, teacherId: In(teacherIds) },
    });

    // 构建教师志愿映射
    const teacherVolunteerMap = new Map<number, any[]>();
    allTeacherVolunteers.forEach(tv => {
      if (!teacherVolunteerMap.has(tv.teacherId)) {
        teacherVolunteerMap.set(tv.teacherId, []);
      }
      teacherVolunteerMap.get(tv.teacherId)!.push(tv);
    });

    // 5. 筛选出还有空闲位置的教师
    const availableTeachers = teachers
      .map(teacher => {
        const currentTeams = teacherCountMap.get(teacher.teacherId) || 0;
        const availableSlots = teacherMax - currentTeams;

        return {
          teacherId: teacher.teacherId,
          teacherName: teacher.user?.nickName || '未知教师',
          teachId: teacher.teachId || '',
          maxTeams: teacherMax,
          currentTeams,
          availableSlots,
          volunteers: teacherVolunteerMap.get(teacher.teacherId) || []
        };
      })
      .filter(teacher => teacher.availableSlots > 0) // 只返回有空闲位置的教师
      .sort((a, b) => b.availableSlots - a.availableSlots); // 按剩余名额降序排序

    return resBuild.data(availableTeachers);
  }

  /**
   * 导出分配结果
   */
  async export(groupId: number) {
    // 1. 获取分组信息
    const group = await this.groupRepository.findOne({
      where: { groupId, delFlag: '0' },
    });

    if (!group) {
      throw new Error('分组不存在');
    }

    // 2. 查询该分组下已分配的队伍（selectFlag = '1'）
    const teams = await this.teamRepository.find({
      where: { groupId, selectFlag: '1', delFlag: '0' },
      relations: ['teamTeachers', 'teamTeachers.teacher', 'teamTeachers.teacher.user'],
    });

    // 3. 构建导出数据
    const exportData = [];

    for (const team of teams) {
      // 找到被选中的教师（selectFlag = 1）
      const selectedTeamTeacher = team.teamTeachers?.find(tt => tt.selectFlag === 1);
      const teacherName = selectedTeamTeacher?.teacher?.user?.nickName || '未分配';

      // 查询队长信息（isLeader = '1'）
      const leaderRelation = await this.studentTeamRepository.findOne({
        where: { teamId: team.teamId, isLeader: '1' },
        relations: ['student', 'student.user'],
      });

      const leaderName = leaderRelation?.student?.user?.nickName || '无队长';

      exportData.push({
        groupName: group.groupName,
        teamName: team.teamName,
        leaderName: leaderName,
        teacherName: teacherName,
      });
    }

    // 4. 定义表头
    const headers = [
      { label: '分组名', prop: 'groupName' },
      { label: '队伍名', prop: 'teamName' },
      { label: '队长', prop: 'leaderName' },
      { label: '老师名称', prop: 'teacherName' },
    ];

    // 5. 调用 Excel 导出服务
    return this.downloadExcelService.downloadExcel({
      headers,
      data: exportData,
      sheetName: '师生分配结果',
    });
  }
}
